package com.feather.tenia.adapter;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 经分析和权衡，Execute Sql 不适合做cache。
 */
public class PrepareExecuteSql {
	public String			originalSql;
	public String			prepareSql;
	public List<String[]>	placeholder;

	public PrepareExecuteSql() {

	}

	public PrepareExecuteSql(String originalSql, String prepareSql, List<String[]> placeholder) {
		this.originalSql = originalSql;
		this.prepareSql = prepareSql;
		this.placeholder = placeholder;
	}

	// 为了效率, sql语句及 placeholder写法正确性不做检测分析.
	public static PrepareExecuteSql prepare(String sql, Set<Map.Entry<String, Object>> paramSet, Map<String, Object> matchedParams, boolean placeholderEmptyEqualsNull) {
		List<String[]> placeholder = new ArrayList<String[]>();
		String prepareSql = prepare(sql, placeholder, paramSet, matchedParams, placeholderEmptyEqualsNull);
		return new PrepareExecuteSql(sql, prepareSql, placeholder);
	}

	private static String prepare(String sql, List<String[]> placeholder, Set<Map.Entry<String, Object>> paramSet, Map<String, Object> matchedParams, boolean placeholderEmptyEqualsNull) {
		int sqlLength = sql.length();
		if (paramSet != null && paramSet.size() > 0) {
			String conditionPrev = "@<";
			String conditionBody = ">{";
			String conditionRear = "}@";
			int conditionPrevAt = sql.indexOf(conditionPrev);
			if (conditionPrevAt != -1) {
				int conditionFrom = conditionPrevAt + conditionPrev.length();
				if (conditionFrom < sqlLength) {
					int conditionBodyAt = sql.indexOf(conditionBody, conditionFrom);
					if (conditionBodyAt != -1) {
						int conditionBodyFrom = conditionBodyAt + conditionBody.length();
						if (conditionBodyFrom < sqlLength) {
							int conditionRearAt = sql.indexOf(conditionRear, conditionBodyFrom);
							if (conditionRearAt != -1) {
								String condition = sql.substring(conditionFrom, conditionBodyAt).trim();
								boolean exists = false;
								for (Map.Entry<String, Object> entry : paramSet) {
									String key = entry.getKey();
									if (condition.equalsIgnoreCase(key)) {
										if (placeholderEmptyEqualsNull) {
											Object value = entry.getValue();
											if (value instanceof String[]) {
												String[] arr = (String[]) value;
												value = arr.length > 0 ? arr[0] : null;
											}
											if (value != null && value.toString().length() > 0)
												exists = true;
										} else {
											exists = true;
										}
										break;
									}
								}

								StringBuilder builder = new StringBuilder(sql.substring(0, conditionPrevAt));
								if (exists) {
									builder.append(sql.substring(conditionBodyFrom, conditionRearAt));
								}
								builder.append(sql.substring(conditionRearAt + conditionRear.length()));
								sql = builder.toString();

								return prepare(sql, placeholder, paramSet, matchedParams, placeholderEmptyEqualsNull);
							}
						}
					}
				}
			}
		}

		String poundSignPrev = "#{";
		String poundSignRear = "}";
		int poundSignPrevLength = poundSignPrev.length();
		int poundSignRearLength = poundSignRear.length();
		int poundSignPrevAt = sql.indexOf(poundSignPrev);
		int poundSignSubFrom = poundSignPrevAt + poundSignPrevLength;
		int poundSignRearAt = poundSignPrevAt != -1 && poundSignSubFrom < sqlLength ? sql.indexOf(poundSignRear, poundSignSubFrom) : -1;

		String dollarSignPrev = "${";
		String dollarSignRear = "}";
		int dollarSignPrevLength = dollarSignPrev.length();
		int dollarSignRearLength = dollarSignRear.length();
		int dollarSignPrevAt = sql.indexOf(dollarSignPrev);
		int dollarSignSubFrom = dollarSignPrevAt + dollarSignPrevLength;
		int dollarSignRearAt = dollarSignPrevAt != -1 && dollarSignSubFrom < sqlLength ? sql.indexOf(dollarSignRear, dollarSignSubFrom) : -1;

		if (poundSignRearAt != -1 && poundSignPrevAt != -1 && (dollarSignPrevAt == -1 || dollarSignPrevAt > poundSignPrevAt)) {
			String substr = sql.substring(poundSignSubFrom, poundSignRearAt);
			String[] array = substr.split(",");
			String name = array[0].trim();
			String type = null;
			if (array.length > 1) {
				String[] jdbcType = array[1].split("=");
				if (jdbcType.length == 2 && jdbcType[0].trim().equalsIgnoreCase("jdbcType"))
					type = jdbcType[1].trim();
			}

			placeholder.add(new String[] { name, type });

			StringBuilder builder = new StringBuilder(sql.substring(0, poundSignPrevAt));
			builder.append("?");
			builder.append(sql.substring(poundSignRearAt + poundSignRearLength));
			sql = builder.toString();

			return prepare(sql, placeholder, paramSet, matchedParams, placeholderEmptyEqualsNull);
		} else if (dollarSignRearAt != -1 && dollarSignPrevAt != -1 && (poundSignPrevAt == -1 || poundSignPrevAt > dollarSignPrevAt)) {
			String name = sql.substring(dollarSignSubFrom, dollarSignRearAt);
			Object value = null;
			if (paramSet != null) {
				for (Map.Entry<String, Object> entry : paramSet) {
					String key = entry.getKey();
					if (name.equalsIgnoreCase(key)) {
						value = entry.getValue();
						break;
					}
				}
			}
			StringBuilder builder = new StringBuilder(sql.substring(0, dollarSignPrevAt));
			if (value != null) {
				builder.append(value.toString());
			}
			builder.append(sql.substring(dollarSignRearAt + dollarSignRearLength));
			sql = builder.toString();
			if (matchedParams != null) {
				matchedParams.put(name, value);
			}
			return prepare(sql, placeholder, paramSet, matchedParams, placeholderEmptyEqualsNull);
		}
		return sql;
	}
}
